﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml.Linq;

namespace Xom.Generator
{
    /// <summary>
    /// Contains all Informations about the XSD File
    /// </summary>
    public class XsdDocument
    {
        /// <summary>
        /// Loaded XSD File
        /// </summary>
        public XDocument XsdFile { get; set; }

        /// <summary>
        /// List of all defined complex Types
        /// </summary>
        public Dictionary<string, XsdComplexType> ComplexTypes;

        /// <summary>
        /// List of all defined simple Types
        /// </summary>
        public Dictionary<string, XsdSimpleType> SimpleTypes;

        /// <summary>
        /// List of all defined enumeration Types
        /// </summary>
        public Dictionary<string, XsdEnumerationType> EnumerationTypes; 

        /// <summary>
        /// List of all defined NameSpaces
        /// </summary>
        public Dictionary<string, string> NameSpaces;

        /// <summary>
        /// XSD Name
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Generated Code
        /// </summary>
        public string Code { get; set; }

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="xsdfile">XSD File</param>
        /// <param name="name">Name of the XSD and Code File</param>
        public XsdDocument(XDocument xsdfile, string name)
        {
            XsdFile = xsdfile;

            //example: file:///C:/Test/Test123.xsd
            if (!string.IsNullOrEmpty(name))
            {
                Name = name.Substring(name.LastIndexOf("/") + 1);
                Name = Name.Substring(0, Name.LastIndexOf("."));
            }

            ComplexTypes = new Dictionary<string, XsdComplexType>();
            SimpleTypes = new Dictionary<string, XsdSimpleType>();
            EnumerationTypes = new Dictionary<string, XsdEnumerationType>();
            NameSpaces = new Dictionary<string, string>();
        }
    }

    public interface IXsdType
    {
        /// <summary>
        /// Modified Class/Interface/Enum Name
        /// </summary>
        string Name { get; set; }

        /// <summary>
        /// Original Xml Name
        /// </summary>
        string XmlName { get; set; }

        /// <summary>
        /// Reference to the original XSD Node
        /// </summary>
        XElement XsdNode { get; set; }

        /// <summary>
        /// Summary Data
        /// </summary>
        string Description { get; set; }
    }

    /// <summary>
    /// Base Class for all XSD Types
    /// </summary>
    public abstract class XsdType : IXsdType
    {
        private string _name;

        /// <summary>
        /// Modified Class/Interface/Enum Name
        /// </summary>
        public string Name
        {
            get { return _name; }
            set
            {
                _name = XsdHelper.FilterName(value);
            }
        }

        /// <summary>
        /// Original Xml Name
        /// </summary>
        public string XmlName { get; set; }

        /// <summary>
        /// Reference to the original XSD Node
        /// </summary>
        public XElement XsdNode { get; set; }

        /// <summary>
        /// Summary Data
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="name"></param>
        protected XsdType(string name)
        {
            XmlName = name;
            Name = name;
            Description = string.Empty;
        }
    }

    public class XsdEnumerationType : XsdType, IXsdType
    {
        /// <summary>
        /// List of all Enumerations
        /// </summary>
        public IList<string> Enumerations { get; set; }

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="name">Name of the Type</param>
        public XsdEnumerationType(string name) : base(name)
        {
            Enumerations = new List<string>();
        }
    }

    public class XsdSimpleType : XsdType, IXsdType
    {
        /// <summary>
        /// C# Type Name
        /// </summary>
        public string TypeName { get; set; }

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="name"></param>
        public XsdSimpleType(string name): base(name)
        {
        }
    }

    public class XsdComplexType : XsdType, IXsdType
    {
        /// <summary>
        /// Interface Name
        /// </summary>
        public string InterfaceName
        {
            get { return "I" + Name; }
        }

        /// <summary>
        /// List of all defined Properties
        /// </summary>
        public IDictionary<string, XsdTypeReference> Properties { get; set; }

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="name"></param>
        public XsdComplexType(string name): base(name)
        {
            Properties = new Dictionary<string, XsdTypeReference>();
        }
    }

    public class XsdTypeReference
    {
        /// <summary>
        /// Public Name
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Is Property a XML Attribute
        /// </summary>
        public bool IsAttribute { get; set; }

        /// <summary>
        /// Is Property required
        /// </summary>
        public bool IsRequired { get; set; }

        /// <summary>
        /// Original XML Name
        /// </summary>
        public string XmlName { get; set; }

        /// <summary>
        /// Is IList Property 
        /// </summary>
        public bool IsList { get; set; }

        /// <summary>
        /// Simple or Complex Type
        /// </summary>
        public bool IsSimpleType { get; set; }

        /// <summary>
        /// Xml Sewuence Index
        /// </summary>
        public int Index { get; set; }

        /// <summary>
        /// Description
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// Type Interface Name
        /// </summary>
        public string TypeInterfaceName { get; set; }

        /// <summary>
        /// Simple Type Name
        /// </summary>
        public string TypeName { get; set; }

        /// <summary>
        /// Private Name
        /// </summary>
        public string PrivateName
        {
            get
            {
                return "_" + Char.ToLowerInvariant(Name[0]) + Name.Substring(1);
            }
        }

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="isAttribute"></param>
        /// <param name="isRequired"></param>
        /// <param name="isList"></param>
        /// <param name="index"></param>
        public XsdTypeReference(string name, bool isAttribute, bool isRequired, bool isList, int index)
        {
            Name = XsdHelper.FilterName(name);
            if (isList) Name = Name + "s";

            XmlName = name;
            IsAttribute = isAttribute;
            Index = index;
            IsList = isList;
            IsRequired = isRequired;
            Description = string.Empty;
            TypeInterfaceName = string.Empty;
            TypeName = string.Empty;
        }

        /// <summary>
        /// Returns IsAttribute as string 
        /// </summary>
        /// <returns></returns>
        public string GetIsAttributeAsString()
        {
            return IsAttribute.ToString(CultureInfo.InvariantCulture).ToLower();
        }

        /// <summary>
        /// Returns IsRequired as String
        /// </summary>
        /// <returns></returns>
        public string GetIsRequiredAsString()
        {
            return IsRequired.ToString(CultureInfo.InvariantCulture).ToLower();
        }
    }
}